#include <QtTest>

#include "../tests_common.h"

#include <QString>
#include <QBitArray>


using namespace BitWin;


//---- Auxiliary staff

class UserIntegral
{
public:

    UserIntegral(int)
    {}

    static size_t size()
    {
        return 7;
    }
};


// Define explicit specializations related to UserIntegral type
// in BitWin namespace.
namespace BitWin
{


template <>
QString PRowTextMaker::IntegralData<UserIntegral>::at(int index) const
{
    if (index & 1)
        return QString("U");
    else
        return QString("Z");
}


template <>
size_t PRowTextMaker::IntegralData<UserIntegral>::size() const
{
    return UserIntegral::size();
}


}; // BitWin namespace end


//---- Test suit itself

class TestRowText: public QObject
{

    Q_OBJECT

private slots:

    void testLiteralAt_data();
    void testLiteralAt();

    void testLiteralSize_data();
    void testLiteralSize();

    void testNumerical_data();
    void testNumerical();

    void testAtBad_data();
    void testAtBad();

    void testMid_data();
    void testMid();

    void testViewIn_data();
    void testViewIn();

    void testUseQVariant_data();
    void testUseQVariant();

};


void TestRowText::testLiteralAt_data()
{
    QTest::addColumn<PRowText>("text");
    QTest::addColumn<int>("index");
    QTest::addColumn<QString>("result");
    
    QTest::newRow("String text at(0)")
        << STRING_TEXT("QWERTY")  << 0 << "Q"
    ;
    QTest::newRow("String text at(1)")
        << STRING_TEXT("QWERTY")  << 1 << "W"
    ;
    QTest::newRow("String text at(2)")
        << STRING_TEXT("QWERTY")  << 2 << "E"
    ;
    
    QTest::newRow("List text at(0)")
        << LIST_TEXT("QWERTY" << "ASDF" << "ZXCV") << 0 << "QWERTY"
    ;
    QTest::newRow("List text at(1)")
        << LIST_TEXT("QWERTY" << "ASDF" << "ZXCV") << 1 << "ASDF"
    ;
    QTest::newRow("List text at(2)")
        << LIST_TEXT("QWERTY" << "ASDF" << "ZXCV") << 2 << "ZXCV"
    ;
    
    QTest::newRow("std::string at(0)")
        << fromLiteral<std::string>(std::string("fo0")) << 0 << "f"
    ;
    QTest::newRow("std::string at(1)")
        << fromLiteral<std::string>(std::string("fo0")) << 1 << "o"
    ;
    QTest::newRow("std::string at(2)")
        << fromLiteral<std::string>(std::string("fo0")) << 2 << "0"
    ;
}

void TestRowText::testLiteralAt()
{
    QFETCH(PRowText, text);
    QFETCH(int, index);
    QFETCH(QString, result);
    QCOMPARE(text->at(index), result);
}

void TestRowText::testLiteralSize_data()
{
    QTest::addColumn<PRowText>("text");
    QTest::addColumn<int>("result");
    
    QTest::newRow("String text size") << STRING_TEXT("zxcvb") << 5;
    QTest::newRow("Empty string text size") << STRING_TEXT("") << 0;
    QTest::newRow("Null string text size")
        << STRING_TEXT(QString()) << 0
    ;
    
    QTest::newRow("List text size")
        << LIST_TEXT("QWERTY" << "ASDF" << "ZXCV") << 3
    ;
    QTest::newRow("Empty list text size") << EMPTY_LIST_TEXT << 0;
    
    QTest::newRow("std::string size")
        << fromLiteral<std::string>("foo") << 3
    ;
    QTest::newRow("empty std::string size")
        << fromLiteral<std::string>("") << 0
    ;
    QTest::newRow("null std::string size")
        << fromLiteral<std::string>(std::string()) << 0
    ;
    
    QTest::newRow("Null PRowText size()") << PRowText() << 0;
}

void TestRowText::testLiteralSize()
{
    QFETCH(PRowText, text);
    QFETCH(int, result);
    QCOMPARE(text->size(), result);
}

void TestRowText::testNumerical_data()
{
    QTest::addColumn<PRowText>("data");
    QTest::addColumn<PRowText>("expected");
    
    QTest::newRow("char(0)")
        << fromIntegral<char>(0)
        << STRING_TEXT("00000000")
    ;
    QTest::newRow("char(0x0A)")
        << fromIntegral<char>(0x0A)
        << STRING_TEXT("01010000")
    ;
    QTest::newRow("char(0xCA)")
        << fromIntegral<char>(0xCA)
        << STRING_TEXT("01010011")
    ;
    QTest::newRow("signed short(0xFADE)")
        << fromIntegral<signed short>(0xFADE)
        << STRING_TEXT("0111101101011111")
    ;
    QTest::newRow("unsigned short(0xF00D)")
        << fromIntegral<unsigned short>(0xF00D)
        << STRING_TEXT("1011000000001111")
    ;
    QTest::newRow("UserIntegral")
        << fromIntegral<UserIntegral>(0)
        << STRING_TEXT("ZUZUZUZ")
    ;
    
    {
        QBitArray a(9);
        a.setBit(0);
        a.setBit(2);
        a.setBit(5);
        a.setBit(7);
        QTest::newRow("QBitArray")
            << fromBitset<QBitArray>(a)
            << STRING_TEXT("101001010")
        ;
    }
    
    QTest::newRow("std::bitset")
        << fromBitset<std::bitset<12> >(std::bitset<12>(0xF0D))
        << STRING_TEXT("101100001111")
    ;
}

void TestRowText::testNumerical()
{
    QFETCH(PRowText, data);
    QFETCH(PRowText, expected);
    QCOMPARE(data, expected);
}

void TestRowText::testAtBad_data()
{
    QTest::addColumn<PRowText>("text");
    QTest::addColumn<int>("index");
    
    QTest::newRow("at(-1)")
        << STRING_TEXT("QWERTY") << (-1)
    ;
    QTest::newRow("at(-2)")
        << STRING_TEXT("QWERTY") << (-2)
    ;
    QTest::newRow("at(length())")
        << STRING_TEXT("QWERTY") << 6
    ;
    QTest::newRow("at(length() + 1)")
        << STRING_TEXT("QWERTY") << 7
    ;
    QTest::newRow("at(0) for empty string")
        << STRING_TEXT("") << 0
    ;
    QTest::newRow("at(1) for empty string")
        << STRING_TEXT("") << 1;
    QTest::newRow("at(0) for Null string")
        << STRING_TEXT(QString()) << 0
    ;
    QTest::newRow("at(1) for Null string")
        << STRING_TEXT(QString()) << 1
    ;
    QTest::newRow("at(-1) for Null string")
        << STRING_TEXT(QString()) << -1
    ;
    
    QTest::newRow("Null PRowText at(-1)") << PRowText() << -1;
    QTest::newRow("Null PRowText at(0)") << PRowText() << 0;
    QTest::newRow("Null PRowText at(1)") << PRowText() << 1;
    
    QTest::newRow("Mid at(-1)")
        << STRING_TEXT("ABC")->mid(BitRange(0, 3)) << -1
    ;
    QTest::newRow("Empty mid at(0)")
        << STRING_TEXT("ABC")->mid(BitRange()) << 0
    ;
    QTest::newRow("Mid at(length)")
        << STRING_TEXT("ABC")->mid(BitRange(0, 3)) << 3
    ;
    
    QTest::newRow("View at(-1)")
        << STRING_TEXT("ABC")->viewIn(BitRange(0, 3)) << -1
    ;
    QTest::newRow("View at(length)")
        << STRING_TEXT("ABC")->viewIn(BitRange(0, 3)) << 3
    ;
}

void TestRowText::testAtBad()
{
    QFETCH(PRowText, text);
    QFETCH(int, index);
    QVERIFY(text->at(index).isEmpty());
}

void TestRowText::testMid_data()
{
    QTest::addColumn<PRowText>("text");
    QTest::addColumn<BitRange>("range");
    QTest::addColumn<PRowText>("result");
    
    QTest::newRow("null range")
        << STRING_TEXT("abcd") << BitRange() << STRING_TEXT("")
    ;
    QTest::newRow("full range")
        << STRING_TEXT("abcd") << BitRange(0, 4)
        << STRING_TEXT("abcd")
    ;
    QTest::newRow("middle range")
        << STRING_TEXT("abcdefg") << BitRange(2, 5)
        << STRING_TEXT(  "cde")
    ;
    QTest::newRow("rhs overrange")
        << STRING_TEXT("abcdefg") << BitRange(2, 10)
        << STRING_TEXT(  "cdefg   ")
    ;
}

void TestRowText::testMid()
{
    QFETCH(PRowText, text);
    QFETCH(BitRange, range);
    
    QFETCH(PRowText, result);
    QCOMPARE(text->mid(range), result);
}

void TestRowText::testViewIn_data()
{
    QTest::addColumn<PRowText>("text");
    QTest::addColumn<BitRange>("range");
    QTest::addColumn<PRowText>("result");
    
    QTest::newRow("null range")
        << STRING_TEXT("abcde") << BitRange()
        << STRING_TEXT("     ")
    ;
    QTest::newRow("full range")
        << STRING_TEXT("abcde") << BitRange(0, 5)
        << STRING_TEXT("abcde")
    ;
    QTest::newRow("mid range")
        << STRING_TEXT("abcdefgh") << BitRange(2, 5)
        << STRING_TEXT("  cde   ")
    ;
    QTest::newRow("rhs overrange")
        << STRING_TEXT("abcdefgh") << BitRange(2, 10)
        << STRING_TEXT("  cdefgh")
    ;
}

void TestRowText::testViewIn()
{
    QFETCH(PRowText, text);
    QFETCH(BitRange, range);
    
    QFETCH(PRowText, result);
    QCOMPARE(text->viewIn(range), result);
}

void TestRowText::testUseQVariant_data()
{
    QTest::addColumn<PRowText>("text");
    QTest::addColumn<int>("index");
    QTest::addColumn<QString>("result");
    
    QTest::newRow("String text at(1)")
        << STRING_TEXT("zxcvb") << 1 << QString("x")
    ;
    QTest::newRow("List text at(2)")
        << LIST_TEXT("QWERTY" << "ASDF" << "ZXCV") << 2
        << QString("ZXCV")
    ;
    // TODO: add test cases for MidDecorator, ViewInDecorator and
    // numerical data
}

void TestRowText::testUseQVariant()
{
    QVariant qvar;
    int data_index = -1;
    
    {
        QFETCH(PRowText, text);
        QFETCH(int, index);
        data_index = index;
        qvar.setValue(text);
    }
    
    QVERIFY(qvar.canConvert<PRowText>());
    
    QFETCH(QString, result);
    QCOMPARE(qvar.value<PRowText>()->at(data_index), result);
}


QTEST_MAIN(TestRowText)


#include "main.moc"
